package manager

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis/v8"
	"sieve_engine/lib/config"
	"sieve_engine/lib/log"
	"sync/atomic"
	"time"
)

var (
	mConfig           = newManagerConfig()
	configRedisParams = RedisParams{
		Addr:      *config.String("sieve.config.host", "", "redis host"),
		Pwd:       *config.String("sieve.config.pwd", "", "redis pwd"),
		Channel:   *config.String("sieve.config.channel", "", "redis channel"),
		DB:        *config.Int("sieve.config.db", 0, "redis db"),
		Prefix:    *config.String("sieve.config.prefix", "", "redis prefix"),
		Separator: *config.String("sieve.config.separator", "", "redis separator"),
	}
)

const (
	keyCluster = "cluster"
	keyTask    = "task"
)

type managerConfig struct {
	redisCli      *redis.Client
	initFinished  *atomic.Bool
	clusterConfig *ClusterConfig
	taskConfig    *TaskConfig
}

func newManagerConfig() *managerConfig {

	m := &managerConfig{
		clusterConfig: new(ClusterConfig),
		taskConfig:    new(TaskConfig),
		initFinished:  new(atomic.Bool),
	}
	go m.lazyInit()
	return m
}

func (config *managerConfig) lazyInit() {

	lazyInitOnce.Do(func() {
		config.redisCli = redis.NewClient(&redis.Options{
			Addr:         configRedisParams.Addr,
			Password:     configRedisParams.Pwd, // 如果有密码，请填写对应的密码
			DB:           configRedisParams.DB,  // 选择要使用的数据库
			WriteTimeout: time.Minute * 5,
			ReadTimeout:  time.Minute * 5,
			DialTimeout:  time.Minute * 5,
		})
	})

	if err := config.sync(); err != nil {
		log.Err(fmt.Sprintf("同步引擎配置参数失败:%v", err))
	}
	config.initFinished.Store(true)

	return
}

func (config *managerConfig) flushTask() error {

	return config.flush(keyTask, true)
}

func (config *managerConfig) flushCluster() error {

	return config.flush(keyCluster, false)
}

func (config *managerConfig) flush(key string, force bool) error {

	var (
		err    error
		result []interface{}
		ctx    = context.Background()
	)
	if !force {
		result, err = config.redisCli.HMGet(ctx, configRedisParams.Channel, key).Result()
		if err != nil {
			return err
		}
	}

	var (
		needMerge bool
		value     string
	)

	if len(result) > 0 {
		needMerge = true
		value = fmt.Sprintf("%v", result[0])
		if value == "<nil>" {
			needMerge = false
		}
	}

	var (
		setValue string
	)
	if key == keyCluster {

		if needMerge {
			var clusterConfig = &clusterConfigSync{}
			_ = json.Unmarshal([]byte(value), clusterConfig)
			config.clusterConfig.merge(clusterConfig)
		}
		setValue = config.clusterConfig.transfer().string()
	}

	if key == keyTask {
		if needMerge {
			var taskConfig = &taskConfigSync{}
			_ = json.Unmarshal([]byte(value), taskConfig)
			config.taskConfig.merge(taskConfig)
		}

		setValue = config.taskConfig.transfer().string()
	}
	mapValue := map[string]interface{}{
		key: setValue,
	}

	if err := config.redisCli.HMSet(ctx, configRedisParams.Channel, mapValue).Err(); err != nil {
		return err
	}

	return nil
}

func (config *managerConfig) sync() error {

	var (
		m   = make(map[string]interface{})
		ctx = context.Background()
	)

	if result, err := config.redisCli.HGetAll(ctx, configRedisParams.Channel).Result(); err != nil {
		return err
	} else {

		for k, v := range result {
			m[k] = v
		}

		if v, ok := m[keyCluster]; ok {
			var clusterConfig = &clusterConfigSync{}
			if err := json.Unmarshal([]byte(fmt.Sprintf("%v", v)), clusterConfig); err == nil {
				config.clusterConfig.sync(clusterConfig)
			}
		}

		if v, ok := m[keyTask]; ok {
			var taskConfig = &taskConfigSync{}
			if err := json.Unmarshal([]byte(fmt.Sprintf("%v", v)), taskConfig); err == nil {
				config.taskConfig.sync(taskConfig)
			}
		} else {
			setValue := getDefaultTaskConfig().transfer().string()

			mapValue := map[string]interface{}{
				keyTask: setValue,
			}
			if err := config.redisCli.HMSet(ctx, configRedisParams.Channel, mapValue).Err(); err != nil {
				return err
			}
		}
	}

	return nil
}
